////////////////////////////////////////////////////////////////////////////////////
//
//
//							MiTAC Corporation
//				NO. 187, TIDING BLVD., SEC. 2, TAIPEI, TAIWAN
//
// 			(c) Copyright 2006, MiTAC Corporation, Taipei, Taiwan
//						All rights reserved.
//-------------------------------------------------------------------------------------
//
//	File:	mmu.c
//	Programer: Visual.Wei
//	Date: 2006-3-20
//	Description:	setup MMU pages
//
//	Page directory is always indexed by anding virtual address[31:20],12 bits.
//	while it lefted 20 bits to equally divide 4GB space into 1MB subspaces.
//	And there are maximum of 2^12=4096 page directory entries.
//	So Page directory takes up 4096x4=0x4000=16 KBytes.
////////////////////////////////////////////////////////////////////////////////////
#include <windows.h>
#include <bsp.h>
#include <oal_memory.h>
#include "kernel_asm.h"

#define CACHE 8
#define BUFFER 4

/////////////////////////////////////////////////////////////////////////////
//private:
//page directory base address,aligned by 0x4000
#define PDIR_BASE_MASK 0xffffC000		//[31:14]...18bits
#define pdir_base(pdb) ((pdb)&PDIR_BASE_MASK)

//page directory entry index(offset)
#define PDIR_INDEX_MASK 0xfff00000		//[31:20]...12bits,divide 4GB space into 1MB subspaces
#define PDIR_INDEX_SHIFT 18
#define pdir_index(va) (((va)&PDIR_INDEX_MASK)>>PDIR_INDEX_SHIFT)
/////////////////////////////////////////////////////////////////////////////

//page directory entry address is aligned 4 bytes
#define pdir_entry_addr(pdb,va) (pdir_base(pdb)+pdir_index(va))

//page directory entry type
#define PDIR_ENTRY_COARSE 1
#define PDIR_ENTRY_SECTION 2
#define PDIR_ENTRY_FINE 3

/////////////////////////////////////////////////////////////////////////////
//private:
//page table base address
#define PTAB_BASE_MASK_COARSE 0xfffffc00		//[31:10]...22bits,aligned by 0x400
#define PTAB_BASE_MASK_FINE 0xfffff000			//[31:12]..20bits,aligned by 0x1000
#define ptab_base_coarse(ptb) ((ptb)&PTAB_BASE_MASK_COARSE)
#define ptab_base_fine(ptb) ((ptb)&PTAB_BASE_MASK_FINE)

//page table entry index(offset)
#define PTAB_INDEX_MASK_COARSE 0x000ff000		//[19:12]...8bits,divide 4GB space into 4kB subspaces
#define PTAB_INDEX_SHIFT_COARSE 10
#define ptab_index_coarse(va) (((va)&PTAB_INDEX_MASK_COARSE)>>PTAB_INDEX_SHIFT_COARSE)

#define PTAB_INDEX_MASK_FINE 0x000ffc00		//[19:10]...10bits,divide 4GB space into 1kB subspaces
#define PTAB_INDEX_SHIFT_FINE 8
#define ptab_index_fine(va) (((va)&PTAB_INDEX_MASK_FINE)>>PTAB_INDEX_SHIFT_FINE)
/////////////////////////////////////////////////////////////////////////////

//page table entry address
#define ptab_entry_addr(type,ptb,va) (ptab_base_##type(ptb)+ptab_index_##type(va))

//page table entry type
#define PTAB_ENTRY_LARGE 1
#define PTAB_ENTRY_SMALL 2
#define PTAB_ENTRY_TINY 3
//page table entry flag
#define PTAB_ENTRY_FLAG_BUFFER 4
#define PTAB_ENTRY_FLAG_CACHE 8

//page table entry physical address mask
#define PTAB_ENTRY_PHY_MASK_SECTION 0xfff00000	//aligned 1MB
#define PTAB_ENTRY_PHY_MASK_LARGE 0xffff0000	//aligned 64KB
#define PTAB_ENTRY_PHY_MASK_SMALL 0xfffff000	//aligned 4KB
#define PTAB_ENTRY_PHY_MASK_FINE 0xfffffC00	//aligned 1KB

//1M page region
static void section_dir(UINT32 pdb,UINT32 va,UINT32 pa,UINT32 flag)
{
	UINT32 entry_addr,entry;
	entry_addr=pdir_entry_addr(pdb,va);
	entry=(pa&PTAB_ENTRY_PHY_MASK_SECTION)|PDIR_ENTRY_SECTION|flag;
	OUTREG32((void *)entry_addr,entry);
	RETAILMSG(0,(L"L1: address:0x%x val:0x%x :section\r\n",entry_addr,entry));
}


//4KB page region
static void coarsepage_dir(UINT32 pdb,UINT32 va,UINT32 ptb)
{
	UINT32 entry_addr,entry;
	entry_addr=pdir_entry_addr(pdb,va);
	entry=ptab_base_coarse(ptb)|PDIR_ENTRY_COARSE;
	OUTREG32((void *)entry_addr,entry);
	RETAILMSG(0,(L"L1: address:0x%x val:0x%x :coarse->\r\n",entry_addr,entry));
}

//1KB page region
static void finepage_dir(UINT32 pdb,UINT32 va,UINT32 ptb)
{
	UINT32 entry_addr,entry;
	entry_addr=pdir_entry_addr(pdb,va);
	entry=ptab_base_fine(ptb)|PDIR_ENTRY_FINE;
	OUTREG32((void *)entry_addr,entry);
}


static void do_map_1M(UINT32 va,UINT32 pa,UINT32 flag)
{
	section_dir(PA_L1_PT_BASE,va,pa,flag);
}


static void do_map_64K(UINT32 va,UINT32 pa,UINT32 ptb,UINT32 flag)
{
	UINT32 entry_addr,entry;
	int i;
	coarsepage_dir(PA_L1_PT_BASE,va,ptb);
	entry_addr=ptab_entry_addr(coarse,ptb,va);	//coarse page
	entry=(pa&PTAB_ENTRY_PHY_MASK_LARGE)|PTAB_ENTRY_LARGE|flag;//large page
	for(i=0;i<=0xF;i++)
	{
		OUTREG32((void *)(entry_addr+i*4),entry);
		RETAILMSG(0,(L"L2: address:0x%x val:0x%x :large\r\n",entry_addr+i*4,entry));
	}
}


static void do_map_4K(UINT32 va,UINT32 pa,UINT32 ptb,UINT32 flag)
{
	UINT32 entry_addr,entry;
	coarsepage_dir(PA_L1_PT_BASE,va,ptb);
	entry_addr=ptab_entry_addr(coarse,ptb,va);	//coarse page
	entry=(pa&PTAB_ENTRY_PHY_MASK_SMALL)|PTAB_ENTRY_SMALL|flag;//small page
	OUTREG32((void *)entry_addr,entry);
}

static void do_map_1K(UINT32 va,UINT32 pa,UINT32 ptb,UINT32 flag)
{
	UINT32 entry_addr,entry;
	finepage_dir(PA_L1_PT_BASE,va,ptb);
	entry_addr=ptab_entry_addr(fine,ptb,va);//fine page
	entry=(pa&PTAB_ENTRY_PHY_MASK_FINE)|PTAB_ENTRY_TINY|flag;//tiny page
	OUTREG32((void *)entry_addr,entry);
}

void page_init()
{
	UINT32 i;
	UINT32 data;
	OAL_ADDRESS_TABLE *pTable = g_oalAddressTable;

	_DisableDCache();
	_DisableICache();
	_InvalidateIDCache();
	_InvalidateTLB();
	//init Level-1 page table.set all table entry to 0
	//memset((void *)PA_L1_PT_BASE,0,0x4000);
	//0x1000:0000-0x1400:0000  64MB SDRAM mapping
	while (pTable->size != 0)
	{
		for(i=0;i<pTable->size;i++)
		{
			//directly map
			do_map_1M(pTable->PA+i*0x100000,pTable->PA+i*0x100000,0);
			//cache
			do_map_1M(pTable->CA+i*0x100000,pTable->PA+i*0x100000,CACHE|BUFFER);
			//uncache
			do_map_1M((pTable->CA|OAL_MEMORY_CACHE_BIT)+i*0x100000,pTable->PA+i*0x100000,0);
		}
		pTable++;
	}
	//directly map for SDRAM
	for(i=0;i<64;i++)
		do_map_1M(0x30000000+i*0x100000,0x30000000+i*0x100000,CACHE|BUFFER);//1M

	//Interrupt Vector mapping
	do_map_64K(0xFFFF0000,0x30000000,PA_L2_PT_BASE_FFF,0);//64k


	_page_enable(PA_L1_PT_BASE);
	_EnableICache();
    	_EnableDCache();
	//check magic number
	OUTREG32((void *)0xFFFF0000,0x55bb);
	data=INREG32((void *)0x30000000);
	if(data==0x55bb)
		RETAILMSG (1, (TEXT("MMU OK!!\r\n"),data));
	else
		RETAILMSG (1, (TEXT("MMU Failed!!\r\n"),data));
}

void page_deinit()
{
	_page_disable();
}
